package pl.com.qapps.datastore.base;

import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import pl.com.qapps.datastore.KindIndex;
import pl.com.qapps.datastore.Store;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortPredicate;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.utils.FutureWrapper;

// TODO implement
/**
 * It should use KindStore.
 * 
 * After considering this class it should specify the right query and use Store
 * to get the right table for the result. I don't know yet if the queries are
 * specified (kind/ancestor).
 * 
 * @author kuba
 * 
 */
public class QueryRunner {

	private final ExecutorService async;
	private final Store store;

	public QueryRunner(Store store, ExecutorService dispatcher) {
		this.async = dispatcher;
		this.store = store;
	}

	public Future<QueryResult> runQuery(final Query query, final Transaction txn,
			final FetchOptions opts) {
		System.out.println("running query...");
		System.out.println("	kind: " + query.getKind());
		System.out.println("	keysOnly: " + query.isKeysOnly());
		System.out.println("	ancestor: " + query.getAncestor());
		System.out.println("	filters:");
		for (FilterPredicate filter : query.getFilterPredicates()) {
			System.out.println("		" + filter);
		}
		System.out.println("	sorts:");
		for (SortPredicate sort : query.getSortPredicates()) {
			System.out.println("		" + sort);
		}

		// lets assume its kind query
		if (query.getFilterPredicates().size() < 1) {
			if (query.getKind() == null) {
				throw new UnsupportedOperationException(
						"kindless queries not supported yet");
			}
			System.out.println("performing kind query");

			return new FutureWrapper<QueryResult, QueryResult>(
					async.submit(new Callable<QueryResult>() {

						@Override
						public QueryResult call() throws Exception {
							KindIndex index = store.getKindIndex();
							return index.search(query.getKind(), opts, txn);
						}

					})) {

				@Override
				protected QueryResult wrap(QueryResult result) throws Exception {
					System.out.println("wrapping result from exact query");
					return result;
				}

				@Override
				protected Throwable convertException(Throwable cause) {
					System.out
							.println("converting exception from query (actually just returning):"
									+ cause);
					return cause;
				}

			};
			// old implementation
			// return runInternal(new KindIndex.KindQuery(query.getKind(),
			// opts),
			// txn);

		}

		System.err
				.println("implementation oversight, returning empty query result!!!!!");
		// TODO implement query handling
		// its only temporary, returns NO result
		return new Future<QueryResult>() {

			@Override
			public boolean cancel(boolean arg0) {
				return false;
			}

			@Override
			public QueryResult get() {
				return new QueryResult() {

					@Override
					public int getSkippedResults() {
						return 0;
					}

					@Override
					public int resultSize() {
						return 0;
					}

					@Override
					public Iterable<Entity> results() {
						return Collections.emptyList();
					}
				};
			}

			@Override
			public QueryResult get(long arg0, TimeUnit arg1) {
				return get();
			}

			@Override
			public boolean isCancelled() {
				return false;
			}

			@Override
			public boolean isDone() {
				return true;
			}

		};
	}

	// private Future<QueryResult> runInternal()

	// private Future<QueryResult> runInternal(final ConcreteQuery concrete,
	// final com.google.appengine.api.datastore.Transaction txn) {
	// return async.submit(new Callable<QueryResult>() {
	//
	// @Override
	// public QueryResult call() throws Exception {
	// System.out.println("QueryRunner: running: " + concrete
	// + "  in txn: " + txn);
	// return concrete.fetch(store,
	// null != null ? ((TransactionImpl) txn).raw : null);
	// }
	//
	// });
	// }
}
